<?php
/*
 * Datafeed Processor snippet
 *
 */

// Prepare error handling.
// Set Paths
$mod_path = $modx->config['base_path'] . 'assets/modules/foxyback/';
include_once $mod_path.'config.inc.php';
include_once $mod_path.'foxyback.class.inc.php';
include_once $mod_path.'notify.class.inc.php';
include_once $mod_path.'includes/class.rc4crypt.php';

// Whether to display debugging info.
// This MUST be set to 'false' for production, otherwise FoxyCart will continually resend 
// each feed every minute until it receives a response of just 'foxy'.
$debug = false;

$product_code_tv_name;
$product_price_tv_name;

// Parse the config file.
if (isset($SNIPPET_CONFIG) && is_array($SNIPPET_CONFIG) && is_array($SNIPPET_CONFIG['DatafeedProcessor'])) {
	$config = $SNIPPET_CONFIG['DatafeedProcessor'];

	if (!isset($product_code_tv_name) && isset($config['product_code_tv_name'])) {
		$product_code_tv_name = $config['product_code_tv_name'];
	}
	if (!isset($product_price_tv_name) && isset($config['product_price_tv_name'])) {
		$product_price_tv_name = $config['product_price_tv_name'];
	}

	// Clean up.
	unset($config);
}

// Set defaults.
if (!isset($product_code_tv_name)) $product_code_tv_name = 'product_code';
if (!isset($product_price_tv_name)) $product_price_tv_name = 'product_price';

// Create the necessary objects.
$foxyback = new FoxyBack;
$notify_obj = new Notify;

// See if we were posted to.
if (!isset($_POST['FoxyData'])) {
	// If you're here, you didn't receive the FoxyData post, so error out.
	// This ensures that the transactions will still show up on the next datafeed.
	$output = "Error: no post data\n";
	$notify_obj->sendErrorEmail($output);
	return $output;
}

// Decrypt the data using your $key
$FoxyData_encrypted = urldecode($_POST['FoxyData']);
$FoxyData_decrypted = rc4crypt::decrypt($XML_KEY,$FoxyData_encrypted);

$data = new SimpleXMLElement($FoxyData_decrypted);

// The XML is now an array called $data.
if (!is_object($data)) {
	$output = 'Error: not an object';
	$notify_obj->sendErrorEmail($output);
	return $output;
}

// The status with id "1" is used for all new transactions.
$default_status_id = 1;

// Count the number of successful imports.
$transaction_count = 0;

// Keep track of any errors so they can be emailed at the end.
$error_string = '';

// Create an array mapping product SKUs (codes) to their prices to use for verification 
// of each transaction detail.
$product_price_array  = array();

// Get the template variable ids for the code and price.
$result = $modx->db->select('`id`', 'modx_site_tmplvars', "`name` = '$product_code_tv_name'");
$row = $modx->db->getRow($result);
$product_code_tv = $row['id'];

$result = $modx->db->select('`id`', 'modx_site_tmplvars', "`name` = '$product_price_tv_name'");
$row = $modx->db->getRow($result);
$product_price_tv = $row['id'];

if ($debug) {
	print "got code tv: $product_code_tv\n";
	print "got price tv: $product_price_tv\n";
}

// Verification can only occur if both template variables have been defined.
if ($product_code_tv != '' && $product_price_tv != '') {
	// Get the list of 'product_code' template variable values.
	$result = $modx->db->select('*', 'modx_site_tmplvar_contentvalues', "`tmplvarid` = '$product_code_tv'");
	$product_code_vars = $modx->db->makeArray($result);

	foreach ($product_code_vars as $code_var) {
		$contentid = $code_var['contentid'];
		$code = $code_var['value'];

		// Look for the price tv on this page (ie, that goes with this code).
		$result = $modx->db->select('`value`', 'modx_site_tmplvar_contentvalues', "`tmplvarid` = '$product_price_tv' AND `contentid` = '$contentid'");
		$price = $modx->db->getRow($result);

		if ($price['value'] != '') {
			// Remember this price.
			$product_price_array[$code] = $price['value'];
		}
	}
}

if ($debug) {
	$notify_obj->sendMessageEmail('product price array on import: '.print_r($product_price_array, true), 'FoxyBack import message');
}

// Loop through the transactions.
foreach ($data->{'transactions'}[0]->{'transaction'} as $transaction) {
	// Construct the different specs to use for the db.
	$transaction_spec = array();
	$address_spec = array();
	$customer_spec = array();
	$status_log_spec = array();

	$transaction_id = $transaction->{'id'};
	$customer_id = $transaction->{'customer_id'};
	if ($debug) {print "trans: $transaction_id, cust: $customer_id\n";}

	// Check if this transaction already exists.
	if ($foxyback->transactionExists($transaction_id)) {
        if ($debug) {print "transaction already exists\n";}
		// Skip to the next one in the feed.
		continue;
	}
	
	$customer_exists = $foxyback->customerExists($customer_id);

	$billing_address_id = '';
	if ($customer_exists) {
		// Get the existing address id.
		$customer_view = $foxyback->getCustomerView($customer_id);
		$billing_address_id = $customer_view['address_id'];
	}

	// Add the main address for this customer.
	$address_spec['customer_id'] = $customer_id;
	$address_spec['transaction_id'] = $transaction_id;
	$address_spec['first_name'] = $transaction->{'customer_first_name'};
	$address_spec['last_name'] = $transaction->{'customer_last_name'};
	$address_spec['company'] = $transaction->{'customer_company'};
	$address_spec['address1'] = $transaction->{'customer_address1'};
	$address_spec['address2'] = $transaction->{'customer_address2'};
	$address_spec['city'] = $transaction->{'customer_city'};
	$address_spec['state'] = $transaction->{'customer_state'};
	$address_spec['postal_code'] = $transaction->{'customer_postal_code'};
	$address_spec['country'] = $transaction->{'customer_country'};
	$address_spec['phone'] = $transaction->{'customer_phone'};
	$address_spec['address_type'] = 'BILLING';
	
	if ($customer_exists && $billing_address_id > 0) {
		// Edit the existing primary address.
		if (!$result = $foxyback->editAddress($billing_address_id, $address_spec)) {
			$error_string .= "$transaction_id - Error editing primary address: " . $foxyback->error_info . "\n";
		}
	}
	else {
		if (!$billing_address_id = $foxyback->addAddress($address_spec)) {
			$error_string .= "$transaction_id - Error adding primary address: " . $foxyback->error_info . "\n";
		}
	}

	// Add/edit the customer.
	$customer_spec['customer_id'] = $customer_id;
	$customer_spec['email'] = $transaction->{'customer_email'};
	$customer_spec['ip_address'] = $transaction->{'customer_ip'};
	$customer_spec['password'] = $transaction->{'customer_password'};
	$customer_spec['address_id'] = $billing_address_id;

	if ($customer_exists) {
		if (!$res = $foxyback->editCustomer($customer_id, $customer_spec)) {
			$error_string .= "$transaction_id - Error editing customer: " . $foxyback->error_info . "\n";
		}
	}
	else {
		if (!$res = $foxyback->addCustomer($customer_spec)) {
			$error_string .= "$transaction_id - Error adding customer: " . $foxyback->error_info . "\n";
		}
	}

	// Add the transaction.
	$transaction_spec['transaction_id'] = $transaction_id;
	$transaction_spec['customer_id'] = $customer_id;
	$transaction_spec['transaction_date'] = $transaction->{'transaction_date'};
	$transaction_spec['purchase_order'] = $transaction->{'purchase_order'};
	$transaction_spec['product_total'] = $transaction->{'product_total'};
	$transaction_spec['tax_total'] = $transaction->{'tax_total'};
	$transaction_spec['shipping_total'] = $transaction->{'shipping_total'};
	$transaction_spec['order_total'] = $transaction->{'order_total'};
	$transaction_spec['order_status_id'] = $default_status_id;

	if (!$res = $foxyback->addTransaction($transaction_spec)) {
		$error_string .= "$transaction_id - Error adding transaction: " . $foxyback->error_info . "\n";
	}

	// A status has been set, so log it.
	$status_log_spec['customer_id'] = $customer_id;
	$status_log_spec['transaction_id'] = $transaction_id;
	$status_log_spec['status_id'] = $default_status_id;
	$status_log_spec['add_date'] = $foxyback->formatMySQLDate();
	$status_log_spec['notes'] = 'Initial order placement';
	
	if (!$res = $foxyback->addStatusLog($status_log_spec)) {
		$error_string .= "$transaction_id - Error adding status log: " . $foxyback->error_info . "\n";
	}

	// shipto_addresses
	$address_spec['address_type'] = 'SHIPPING';
	$address_spec['customer_id'] = $customer_id;
	$address_spec['transaction_id'] = $transaction_id;

	if ($transaction->shipto_addresses[0]->shipto_address) {
		// Use the shipto addresses.
		foreach ($transaction->shipto_addresses[0]->shipto_address as $address) {
			$address_spec['first_name'] = $address->{'shipto_first_name'};
			$address_spec['last_name'] = $address->{'shipto_last_name'};
			$address_spec['company'] = $address->{'shipto_company'};
			$address_spec['address1'] = $address->{'shipto_address1'};
			$address_spec['address2'] = $address->{'shipto_address2'};
			$address_spec['city'] = $address->{'shipto_city'};
			$address_spec['state'] = $address->{'shipto_state'};
			$address_spec['postal_code'] = $address->{'shipto_postal_code'};
			$address_spec['country'] = $address->{'shipto_country'};
			$address_spec['phone'] = $address->{'shipto_phone'};
			$address_spec['service_description'] = $transaction->{'shipto_service_description'};
			$address_spec['subtotal'] = $transaction->{'shipto_subtotal'};
			$address_spec['tax_total'] = $transaction->{'shipto_tax_total'};
			$address_spec['shipping_total'] = $transaction->{'shipto_shipping_total'};
			$address_spec['total'] = $transaction->{'shipto_total'};
			
			if (!$address_id = $foxyback->addAddress($address_spec)) {
				$error_string .= "$transaction_id - Error adding shipto address: " . $foxyback->error_info . "\n";
			}

			// Process the shipto custom_fields
			foreach ($address->shipto_custom_fields[0]->shipto_custom_field as $field) {
				$custom_spec = array();
				$custom_spec['transaction_id'] = $transaction_id;
				$custom_spec['address_id'] = $address_id;
				$custom_spec['customer_id'] = $customer_id;
				$custom_spec['field_name'] = $field->{'shipto_custom_field_name'};
				$custom_spec['field_value'] = $field->{'shipto_custom_field_value'};

				if (!$custom_field_id = $foxyback->addCustomField($custom_spec)) {
					$error_string .= "$transaction_id - Error adding custom field: " . $foxyback->error_info . "\n";
				}
			}
		}
	}
	else if (!empty($address_spec['first_name'])) { // Use the main shipping address.
    $address_spec['first_name'] = $transaction->{'shipping_first_name'};
    $address_spec['last_name'] = $transaction->{'shipping_last_name'};
    $address_spec['company'] = $transaction->{'shipping_company'};
    $address_spec['address1'] = $transaction->{'shipping_address1'};
    $address_spec['address2'] = $transaction->{'shipping_address2'};
    $address_spec['city'] = $transaction->{'shipping_city'};
    $address_spec['state'] = $transaction->{'shipping_state'};
    $address_spec['postal_code'] = $transaction->{'shipping_postal_code'};
    $address_spec['country'] = $transaction->{'shipping_country'};
    $address_spec['phone'] = $transaction->{'shipping_phone'};
    $address_spec['service_description'] = $transaction->{'shipping_service_description'};
    if (!$address_id = $foxyback->addAddress($address_spec)) {
      $error_string .= "$transaction_id - Error adding shipto address: " .
       $foxyback->error_info . "\n";
    }
	}
  else { // Ship to the customer's billing address.
    if (!$address_id = $foxyback->addAddress($address_spec)) {
      $error_string .= "$transaction_id - Error adding shipto address: " .
       $foxyback->error_info . "\n";
    }
  }
		
		if (!$address_id = $foxyback->addAddress($address_spec)) {
			$error_string .= "$transaction_id - Error adding shipto address: " . $foxyback->error_info . "\n";
		}
	}

	// Loop through the products per transaction and process each one.
	foreach ($transaction->transaction_details[0]->transaction_detail as $detail) {
		$detail_spec = array();
		$detail_spec['transaction_id'] = $transaction_id;
		$detail_spec['product_name'] = $detail->{'product_name'};
		$detail_spec['product_price'] = $detail->{'product_price'};
		$detail_spec['product_quantity'] = $detail->{'product_quantity'};
		$detail_spec['product_weight'] = $detail->{'product_weight'};
		$detail_spec['product_code'] = $detail->{'product_code'};
		$detail_spec['subscription_frequency'] = $detail->{'subscription_frequency'};
		$detail_spec['subscription_startdate'] = $detail->{'subscription_startdate'};
		$detail_spec['next_transaction_date'] = $detail->{'next_transaction_date'};
		$detail_spec['shipto'] = $detail->{'shipto'};
		$detail_spec['category_description'] = $detail->{'category_description'};
		$detail_spec['category_code'] = $detail->{'category_code'};
		$detail_spec['product_delivery_type'] = $detail->{'product_delivery_type'};

		if (!$transaction_detail_id = $foxyback->addTransactionDetail($detail_spec)) {
			$error_string .= "$transaction_id - Error adding transaction detail: " . $foxyback->error_info . "\n";
		}

		// Verify the price on this item.
		$product_code = (string) $detail_spec['product_code'];
		$product_price = (string) $detail_spec['product_price'];
#$notify_obj->sendMessageEmail("Got code: $product_code and price: $product_price on trans $transaction_id\nPrice expected: ".$product_price_array[$product_code]."\n".print_r($detail, true), 'FoxyBack - import test');

		if ($product_code != '' && isset($product_price_array[$product_code]) 
				&& $product_price_array[$product_code] != $product_price) {
			// The price does not match what was expected, so flag the order for review.
			$edit_spec = array();
			$edit_spec['flagged'] = 'YES';
			$edit_spec['notes'] = 'The price on this item (' . $detail->{'product_name'} . ") does not match what was expected. \nPrice: " . $detail->{'product_price'} . "\nExpected: " . $product_price_array[$product_code] . "\n";

			// Confirmation message.
			$notify_obj->sendMessageEmail("Found a potential fraud order during datafeed processing!\nTransaction: $transaction_id\n" . $edit_spec['notes'], 'FoxyBack - found fraud order on import!');

			if (!$edit_id = $foxyback->editTransaction($transaction_id, $edit_spec)) {
				$error_string .= "$transaction_id - Error while editing the transaction (to flag it due to price discrepency): " . $foxyback->error_info . "\n";
			}
		}

		// CUSTOM fields - transaction_detail_options
		foreach ($detail->transaction_detail_options[0]->transaction_detail_option as $option) {
			$option_spec = array();
			$option_spec['transaction_detail_id'] = $transaction_detail_id;
			$option_spec['transaction_id'] = $transaction_id;
			$option_spec['product_option_name'] = $option->{'product_option_name'};
			$option_spec['product_option_value'] = $option->{'product_option_value'};
			$option_spec['price_mod'] = $option->{'price_mod'};
			$option_spec['weight_mod'] = $option->{'weight_mod'};

			if (!$option_id = $foxyback->addTransactionDetailOption($option_spec)) {
				$error_string .= "$transaction_id - Error adding transaction detail option: " . $foxyback->error_info . "\n";
			}
		}
	}

	// Proess the transaction custom_fields
	foreach ($transaction->custom_fields[0]->custom_field as $field) {
		$custom_spec = array();
		$custom_spec['transaction_id'] = $transaction_id;
		$custom_spec['customer_id'] = $customer_id;
		$custom_spec['field_name'] = $field->{'custom_field_name'};
		$custom_spec['field_value'] = $field->{'custom_field_value'};

		if (!$custom_field_id = $foxyback->addCustomField($custom_spec)) {
			$error_string .= "$transaction_id - Error adding transaction custom field: " . $foxyback->error_info . "\n";
		}
	}

	// Do some additional processing on this transaction/customer.

	// Done with this transaction.
	$transaction_count++;
}
// Done with all transactions/customers.

// Send debugging message for auditing.
if ($error_string != '') {
	$notify_obj->sendEmail('Errors on FoxyBack datafeed!', "Processed $transaction_count new transactions on ".date('Y-m-d H:i:s')." with the following errors:\n\n$error_string", '', 'software@jeremysaye.com');
}
elseif ($transaction_count > 0) {
	//$notify_obj->sendEmail('FoxyBack datafeed successful', "Processed $transaction_count new transactions on ".date('Y-m-d H:i:s'), '', 'software@jeremysaye.com');
}

// Indicate success.
return 'foxy';

?>

